﻿namespace ClassyBlog.DataAccess
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using DomainObjects;
    using ExtensionMethods;
    using Infrastructure;

    public class LazyArticleLoader<TArticle> : IArticleLoader<TArticle>
        where TArticle : class, IArticle
    {
        private readonly IDictionary<string, Action<TArticle, string, object>>
            knownPropertySetters = BuildSetters();

        private readonly Action<TArticle, string, object> unknownPropertySetter =
            (article, property, value) => article.SetDynamicProperty(property, value);

        public LazyArticleLoader(string path, IFileSystem fileSystem) :
            this(
            path,
            fileSystem,
            new Dictionary<string, Action<TArticle, string, object>>())
        {
        }

        public LazyArticleLoader(
            string path,
            IFileSystem fileSystem,
            IDictionary<string, Action<TArticle, string, object>> propertySetters)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                throw new ArgumentException(
                    TextMessages.LazyArticleLoader_Ctor_Path_Blank, "path");
            }

            if (fileSystem == null)
            {
                throw new ArgumentNullException("fileSystem");
            }

            if (propertySetters == null)
            {
                throw new ArgumentNullException("propertySetters");
            }

            Path = path;
            FileSystem = fileSystem;
            PropertySetters = propertySetters;
        }

        protected string Path { get; private set; }

        protected IFileSystem FileSystem { get; private set; }

        protected IDictionary<string, Action<TArticle, string, object>>
            PropertySetters
        {
            get; private set;
        }

        public virtual void Load(TArticle article)
        {
            if (article == null)
            {
                throw new ArgumentNullException("article");
            }

            var content = FileSystem.ReadAll(Path).Trim();

            article.Body = content;

            var match = RegexExpressions.MetadataMarker.Match(content);

            if (!match.Success)
            {
                return;
            }

            var metadata = content.Substring(0, match.Groups[0].Index).Trim();
            article.Body = content.Substring(match.Groups[0].Index).Trim();

            var mergedPropertySetters = MergeAdditionalSetters();

            article.Load(metadata, mergedPropertySetters, unknownPropertySetter);
        }

        private static IDictionary<string, Action<TArticle, string, object>>
            BuildSetters()
        {
            return new Dictionary<string, Action<TArticle, string, object>>
                       {
                           {
                               "Title",
                               (article, property, value) =>
                                   {
                                       article.Title = value.To<string>();
                                   }
                           },
                           {
                                "MetaKeywords",
                                (article, property, value) =>
                                    {
                                        article.MetaKeywords = value.To<string>();
                                    }
                           },
                           {
                               "MetaDescription",
                               (article, property, value) =>
                                   {
                                       article.MetaDescription = value.To<string>();
                                   }
                           },
                           {
                                "Author",
                                (article, property, value) =>
                                  {
                                      article.Author = value.To<string>();
                                  }
                           },
                           {
                                "AllowComments",
                                (article, property, value) =>
                                {
                                    bool allowComments;

                                    if (bool.TryParse(value.To<string>(), out allowComments))
                                    {
                                        article.AllowComments = allowComments;
                                    }
                                }
                          }
                       };
        }

        private IDictionary<string, Action<TArticle, string, object>>
            MergeAdditionalSetters()
        {
            return knownPropertySetters.Union(PropertySetters)
                .ToDictionary(
                d => d.Key, d => d.Value, StringComparer.OrdinalIgnoreCase);
        }
    }
}